package security.service;

import framework.config.SecurityConfig;
import framework.crypto.GeneralCrypto;
import framework.security.Account;
import framework.security.AccountLoader;
import framework.security.AuthException;
import framework.security.RegApproval;
import framework.security.password.PasswordService;
import framework.utils.RequestUtil;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

public class AccountCheckerDefault implements AccountChecker {

    @Getter
    private final AccountLoader accountLoader;
    @Getter
    private final PasswordService passwordService;
    @Getter
    private final SecurityConfig securityConfig;
    @Getter
    private final GeneralCrypto generalCrypto;

    public AccountCheckerDefault(AccountLoader accountLoader, PasswordService passwordService, SecurityConfig securityConfig, GeneralCrypto generalCrypto) {
        this.accountLoader = accountLoader;
        this.passwordService = passwordService;
        this.securityConfig = securityConfig;
        this.generalCrypto = generalCrypto;
    }

    @Override
    public Account authCheck(String username, String password, String passwordCipher) {
        // login fail limit
        this.checkLoginLimit(username);

        // load account
        Account account = this.loadAccount(username);

        // check account
        this.checkAccountStatus(account);

        // check password
        this.checkAccountPassword(account, password, passwordCipher);

        // return
        return account;
    }

    /**
     * 登录超限检查
     *
     * @param username
     */
    protected void checkLoginLimit(String username) throws AuthException {
        int loginFailLimit = getAccountLoader().loginFailLimit(username);
        if (loginFailLimit > 0) {
            throw new AuthException(AuthException.LOGIN_LIMITED, RequestUtil.getMessageDefault("security.loginFailLimit"
                    , "Login failure too many times, limit login {0} minutes"
                    , ((int) Math.ceil(loginFailLimit / 60d)) + ""));
        }
    }

    /**
     * 验证账户属性
     *
     * @param account
     */
    protected void checkAccountStatus(Account account) throws AuthException {
        // attr check
        try {
            account.statusCheck();
        } catch (AuthException exception) {
            throw exception;
        } catch (Exception exception) {
            throw new AuthException(AuthException.FAILED, exception.getMessage());
        }
        // check registration approved
        if (account.getRegApproval() == null) {
            //
        } else if (RegApproval.Agree.equals(account.getRegApproval())) {
            //
        } else if (RegApproval.Reject.equals(account.getRegApproval())) {
            throw new AuthException(AuthException.APPROVAL_REJECTED, RequestUtil.getMessageDefault("security.userRejectApproved", "Registration approval is rejected, the account is unavailable"));
        } else if (RegApproval.Waiting.equals(account.getRegApproval())) {
            throw new AuthException(AuthException.APPROVAL_PENDING, RequestUtil.getMessageDefault("security.userWaitingApproved", "Waiting for registration approval, the account is unavailable"));
        }
    }

    /**
     * 密码验证
     *
     * @param passwordCipher
     * @param password
     * @param account
     */
    protected void checkAccountPassword(Account account, String password, String passwordCipher) throws AuthException {
        String sourcePassword = this.passwordTransfer(account, password, passwordCipher);
        String salt = account.getPasswordSalt();
        String encodePassword = account.getPassword();
        if (StringUtils.isBlank(account.getPassword())) {
            throw new AuthException(AuthException.PASSWORD_NO_SET, RequestUtil.getMessageDefault("security.passwordNoMatched", "username or password error"));
        }
        if (!getPasswordService().matched(sourcePassword, salt, encodePassword)) {
            throw new AuthException(AuthException.PASSWORD_NO_MATCH, RequestUtil.getMessageDefault("security.passwordNoMatched", "username or password error"));
        }
    }

    /**
     * 加载账号
     *
     * @param username
     * @return
     */
    protected Account loadAccount(String username) {
        Account account = getAccountLoader().loadUserByUsername(username);
        if (account == null) {
            throw new AuthException(AuthException.ACCOUNT_NOT_EXISTS, RequestUtil.getMessageDefault("security.userNotFound", "username or password error"));
        }
        return account;
    }

    /**
     * 加密密码转换
     *
     * @param account
     * @param password
     * @param passwordCipher
     * @return
     */
    protected String passwordTransfer(Account account, String password, String passwordCipher) {
        if (StringUtils.isBlank(password)) {
            throw new AuthException(AuthException.PASSWORD_NO_INPUT, RequestUtil.getMessageDefault("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }

        // decrypt password
        if ("gc".equals(passwordCipher)) {
            try {
                password = getGeneralCrypto().decryptFromBase64AsString(password);
            } catch (Exception exception) {
                throw new AuthException(AuthException.PASSWORD_INVALID, "Password decrypt failed, please refresh page and submit again");
            }
        }

        //
        return password;
    }
}
